package com.pinfly.common.util;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;

import org.apache.log4j.Logger;

import com.pinfly.common.config.BaseConfigConstants;
import com.pinfly.common.crypto.CryptoException;
import com.pinfly.common.crypto.CryptoFactory;

/**
 * EncryptUtil This utility class is intended to be used for encrypting and
 * decrypting
 */
public class EncryptUtil
{
    private static final Logger logger = Logger.getLogger (EncryptUtil.class);
    private static SecretKey m_secretKey = null; // for encryption
    private final static String ALGORITHM = "DESede"; // encryption algorithm

    private EncryptUtil ()
    {
    }

    static
    {
        m_secretKey = getSecretKey ();
    }

    /**
     * getSecretKey Setup the secretkey for encrypting and decrypting
     */
    private static SecretKey getSecretKey ()
    {
        try
        {
            // Create a SecretKeyFactory
            String alg = ALGORITHM;
            SecretKeyFactory skf = SecretKeyFactory.getInstance (alg);

            // Create the key for the KeySpec. A DESede keyspec needs a
            // key that is 24 bytes long. Get the first 6 bytes "DESede"
            // from memory (alg variable). Get the rest of the bytes from
            // the string pinfly...Doing it this way makes it harder
            // for someone to figure out what the 24 bytes are by simple
            // examination of the binary class file.
            byte[] key = (alg + "pi/nf/ly[jboddhtkmltmkmsprskspw]").getBytes ();
            DESedeKeySpec dks = new DESedeKeySpec (key);

            // generate the secret key
            SecretKey sk = skf.generateSecret (dks);
            return sk;
        }
        catch (Exception e)
        {
            logger.warn ("Unable to generate key.", e);
            return null;
        }
    }

    /*
     * encrypt Encrypts the userData.
     * 
     * @param userData
     * 
     * @return String
     * 
     * @throws Exception
     */
    public static String encrypt (String userData) throws Exception
    {
        try
        {
            Cipher c = Cipher.getInstance (ALGORITHM);
            c.init (Cipher.ENCRYPT_MODE, m_secretKey);
            byte[] data = userData.getBytes ();
            byte[] encryptData = c.doFinal (data);
            return HexConvert.convertHexString (encryptData);
        }
        catch (Exception e)
        {
            throw new Exception ("Unable to encrypt data.", e);
        }
    }

    /*
     * decrypt Decrypts the userData.
     * 
     * @param userData
     * 
     * @return String
     * 
     * @throws Exception
     */
    public static String decrypt (String userData) throws Exception
    {
        try
        {
            Cipher c = Cipher.getInstance (ALGORITHM);
            c.init (Cipher.DECRYPT_MODE, m_secretKey);
            byte[] data = HexConvert.convertToBytes (userData);
            byte[] decryptData = c.doFinal (data);
            return new String (decryptData);
        }
        catch (Exception e)
        {
            throw new Exception ("Unable to decrypt data.", e);
        }
    }

    public static String encryptWithCryptTool (String userData) throws CryptoException
    {
        if (StringUtil.isNonEmpty (userData) && !userData.startsWith (BaseConfigConstants.ENCRYPTED_KEY))
        {
            try
            {
                return BaseConfigConstants.ENCRYPTED_KEY + CryptoFactory.getInstance ().encrypt (userData);
            }
            catch (CryptoException e)
            {
                throw e;
            }
        }
        return userData;
    }

    public static String decryptWithCryptTool (String userData) throws CryptoException
    {
        if (StringUtil.isNonEmpty (userData) && userData.startsWith (BaseConfigConstants.ENCRYPTED_KEY))
        {
            try
            {
                return CryptoFactory.getInstance ()
                                    .decrypt (userData.substring (BaseConfigConstants.ENCRYPTED_KEY.length ()));
            }
            catch (CryptoException e)
            {
                throw e;
            }
        }
        return userData;
    }

}
